package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

type mountInfo struct {
	ID         int    `json:"id"`     // 1
	Parent     int    `json:"parent"` // 84
	dev        string // major:minor 0:8
	Root       string `json:"root"`       // /
	Mount      string `json:"Mount"`      // /proc
	Options    string `json:"options"`    // rw,nosuid,nodev,noexec,relatime
	Optional   string `json:"optional"`   // shared:11
	FileSystem string `json:"filesystem"` // ext4
	Source     string `json:"source"`     // /dev/sda1
	Soptions   string `json:"soptions"`

	StDev uint16 `json:"stdev"` // (major << 8) | minor
	Major uint8  `json:"major"`
	Minor uint8  `json:"minor"`
}

func (info *mountInfo) parse() {
	tmp := strings.SplitN(info.dev, ":", 2)

	n, err := strconv.ParseUint(tmp[0], 10, 8)
	assert(err)
	info.Major = uint8(n)

	n, err = strconv.ParseUint(tmp[1], 10, 8)
	assert(err)
	info.Minor = uint8(n)

	info.StDev = (uint16(info.Major) << 8) | uint16(info.Minor)
}

func mountInfoWithOptional(row string, info *mountInfo) (bool, error) {
	format := "%d %d %s %s " +
		"%s %s %s - " +
		"%s %s %s"
	_, err := fmt.Sscanf(row, format,
		&info.ID, &info.Parent, &info.dev, &info.Root,
		&info.Mount, &info.Options, &info.Optional, &info.FileSystem,
		&info.Source, &info.Soptions)
	if err != nil {
		if err.Error() == "input does not match format" {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

func mountInfoNoOptional(row string, info *mountInfo) (bool, error) {
	format := "%d %d %s %s " +
		"%s %s - " +
		"%s %s %s"
	_, err := fmt.Sscanf(row, format,
		&info.ID, &info.Parent, &info.dev, &info.Root,
		&info.Mount, &info.Options, &info.FileSystem,
		&info.Source, &info.Soptions)
	if err != nil {
		if err.Error() == "input does not match format" {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

var mountInfos []mountInfo

func init() {
	f, err := os.Open("/proc/self/mountinfo")
	assert(err)
	defer f.Close()
	r := bufio.NewScanner(f)
	for r.Scan() {
		var info mountInfo
		ok, err := mountInfoWithOptional(r.Text(), &info)
		assert(err)
		if !ok {
			ok, err = mountInfoNoOptional(r.Text(), &info)
			assert(err)
		}
		if !ok {
			// TODO
			continue
		}
		info.parse()
		mountInfos = append(mountInfos, info)
	}
}

func getMountInfo(stdev uint16) (mountInfo, error) {
	for _, info := range mountInfos {
		if info.StDev == stdev {
			return info, nil
		}
	}
	return mountInfo{}, ErrNotfound
}
